package org.jeecg.modules.wlhy.hyevent.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.gavaghan.geodesy.Ellipsoid;
import org.gavaghan.geodesy.GeodeticCalculator;
import org.gavaghan.geodesy.GeodeticCurve;
import org.gavaghan.geodesy.GlobalCoordinates;
import org.jeecg.common.constant.enums.ReportPlatformEnum;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.modules.api.wlhy.app.InitReturnBean;
import org.jeecg.modules.api.wlhy.app.LocalModel;
import org.jeecg.modules.api.wlhy.app.bean.*;
import org.jeecg.modules.api.wlhy.app.utils.LocationSM4Util;
import org.jeecg.modules.api.wlhy.app.utils.SM2Util;
import org.jeecg.modules.api.wlhy.app.utils.Util;
import org.jeecg.modules.middleware.rabbitmq.gen.dto.ScopeRequest;
import org.jeecg.modules.third.jg.report.freight.information.dto.AppInfoDTO;
import org.jeecg.modules.third.jg.report.freight.information.util.sm.SM4Util;
import org.jeecg.modules.third.jg.report.freight.information.util.sm.StringKeyPair;
import org.jeecg.modules.wlhy.company.entity.HyCompanyConfig;
import org.jeecg.modules.wlhy.company.service.IHyCompanyConfigService;
import org.jeecg.modules.wlhy.hydriver.entity.HyDriver;
import org.jeecg.modules.wlhy.hydriver.service.IHyDriverService;
import org.jeecg.modules.wlhy.hyevent.event.AppLocationEvent;
import org.jeecg.modules.wlhy.hyevent.service.LocationListenerService;
import org.jeecg.modules.wlhy.hyvehicle.entity.HyVehicle;
import org.jeecg.modules.wlhy.hyvehicle.service.IHyVehicleService;
import org.jeecg.modules.wlhy.lnglat.entity.HyShippingnoteLnglat;
import org.jeecg.modules.wlhy.lnglat.service.IHyShippingnoteLnglatService;
import org.jeecg.modules.wlhy.report.IReportService;
import org.jeecg.modules.wlhy.report.ReportServiceFactory;
import org.jeecg.modules.wlhy.shipping.entity.HyShippingNote;
import org.jeecg.modules.wlhy.shippingnoteviolation.entity.HyShippingNoteViolation;
import org.jeecg.modules.wlhy.shippingnoteviolation.service.IHyShippingNoteViolationService;
import org.jeecg.modules.wlhy.utils.ByteUtil;
import org.jeecg.modules.wlhy.utils.HyCfgUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.security.Security;
import java.util.*;

/**
 * 监听司机SDK上报位置的业务处理
 */
@Service
@Slf4j
public class LocationListenerServiceImpl implements LocationListenerService {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IHyDriverService hyDriverService;

    @Autowired
    private IHyVehicleService hyVehicleService;

    @Autowired
    private IHyCompanyConfigService hyCompanyConfigService;

    @Autowired
    private IHyShippingnoteLnglatService hyShippingnoteLnglatService;

    @Autowired
    private IHyShippingNoteViolationService shippingNoteViolationService;

    private final String SDK_TEST_URL = "https://debug.wlhy.org.cn/wlhy-exg/sdkPostion/initSendInfoSdk";

    private final String SDK_URL = "https://wlhy.org.cn/wlhy-exg/sdkPostion/initSendInfoSdk";

    @PostConstruct
    void initProvider() {
        Security.addProvider(new BouncyCastleProvider());
    }

    @SneakyThrows
    private boolean initLocationSDKInfo(String appId, String appSecurity, String senderCode, String debug) {

        if (StrUtil.isEmpty(appId)
         || StrUtil.isEmpty(appSecurity)
         || StrUtil.isEmpty(senderCode)) {
            log.error("App配置信息参数错误,  {} {} {}", appId, appSecurity, senderCode);
            return false;
        }

        if (null != this.redisUtil.get(appId + "_publicKey")) {
            return true;
        }

        Map<String, String> keyPairMap = SM2Util.generateKeyPair();

        JSONObject json = new JSONObject();
        json.put("appId", appId);
        json.put("appSecurity", appSecurity);
        json.put("enterpriseSenderCode", senderCode);
        json.put("randomPublicKey", keyPairMap.get("public_key_name"));

        String url = StrUtil.EMPTY;
        if ("debugSystem".equals(debug)) {
            url = SDK_TEST_URL;
        } else {
            url = SDK_URL;
        }
        String result = HttpUtil.post(url, json.toJSONString());
        log.info("上报部平台初始化回执 <- {} ,{}", url, result);

        ResBaseBean resBaseBean = JSONObject.parseObject(result, ResBaseBean.class);
        if (resBaseBean.getSuccess()) {
            EncryptionResponse encryptionResponse = JSONObject.parseObject(resBaseBean.getData().toString(), EncryptionResponse.class);

            //String info = Decrypt.SM2Decrypt(stringKeyPair.getPrivateKey(), encryptionResponse.getEncryptedCode());
            //String plainText = Decrypt.SM4DecryptEcb(info, encryptionResponse.getEncryptedContent());
            String info = Util.byteToHex(SM2Util.decrypt132(Util.hexToByte(keyPairMap.get("private_key_name")), Util.hexToByte(encryptionResponse.getEncryptedCode())));
            String plainText = LocationSM4Util.decryptEcb(info, encryptionResponse.getEncryptedContent());

            //[{"weight":1,
            // "publicKey":"046E0C9C054CA408D29A59D1209D467E73899019881C2617A29
            // B5F49E2AC13B6BB8C3909A214BBB8BCDDEA4912684CD4C07863365944579012E402414FC69CB656",
            // "url":"https://wlhy.org.cn/wlhy-exg/sdkPostion/sendSdk"}]
            List<UrlInfo> urlInfos = JSONObject.parseArray(JSONObject.parseObject(plainText).getString("urlInfoList"), UrlInfo.class);
            log.info("上报部平台初始化URL <- {}", JSONUtil.toJsonStr(urlInfos));

            this.redisUtil.set(appId + "_publicKey", urlInfos.get(0).getPublicKey(), 24 * 60 * 60);
            this.redisUtil.set(appId + "_randomPublicKey", keyPairMap.get("public_key_name"), 24 * 60 * 60);
            this.redisUtil.set(appId + "_randomPrivateKey", keyPairMap.get("private_key_name"), 24 * 60 * 60);
            this.redisUtil.set(appId + "_url", urlInfos.get(0).getUrl(), 24 * 60 * 60);
        } else {
            return false;
        }

        return true;
    }


    @Override
    public void uploadLocation(AppLocationEvent appLocationEvent) throws IOException {

        HyShippingNote hyShippingNote = appLocationEvent.getHyShippingNote();
        HyCompanyConfig hyCompanyConfig = this.hyCompanyConfigService.getOne(new LambdaQueryWrapper<HyCompanyConfig>()
                .eq(HyCompanyConfig::getCompanyId, hyShippingNote.getCompanyId()));
        //生成SDK配置信息
        AppInfoDTO appInfoDTO = AppInfoDTO.builder()
               .appId(hyCompanyConfig.getReportAppid())
               .appSecurity(hyCompanyConfig.getReportAppkey())
               .enterpriseSenderCode(hyCompanyConfig.getReportAppsender())
               .debugSystem("1".equals(hyCompanyConfig.getReportApprelease()) ? "" : "debugSystem")
               .build();

        //初始化SDK上报信息
        if (!initLocationSDKInfo(appInfoDTO.getAppId(), appInfoDTO.getAppSecurity(),
                appInfoDTO.getEnterpriseSenderCode(), appInfoDTO.getDebugSystem())) {
            log.error("上报部平台位置信息参数错误, {}", JSONUtil.toJsonStr(appInfoDTO));
            HyShippingnoteLnglat shippingnoteLnglat =new HyShippingnoteLnglat();
            shippingnoteLnglat.setState("1");
            shippingnoteLnglat.setType(appLocationEvent.getType());
            shippingnoteLnglat.setShippingNum(hyShippingNote.getShippingNoteNumber());
            shippingnoteLnglat.setLongitude(Double.parseDouble(appLocationEvent.getLongitude()));
            shippingnoteLnglat.setLatitude(Double.parseDouble(appLocationEvent.getLatitude()));
            shippingnoteLnglat.setCreateTime(new Date());
            hyShippingnoteLnglatService.save(shippingnoteLnglat);
            return;
        }

        String shippingNoteNumber = hyShippingNote.getShippingNoteNumber();
        String rk = "deviceId:" +  shippingNoteNumber;
        String deviceId;
        if (!redisUtil.hasKey(rk)) {
            deviceId = UUID.randomUUID().toString().replace("-", "");
            redisUtil.set(rk, deviceId,3*24*60*60);
        }else {
            deviceId = (String)redisUtil.get(rk);
        }

        HyVehicle vehicle = this.hyVehicleService.getById(hyShippingNote.getVehicleId());
        HyDriver driver = this.hyDriverService.getById(hyShippingNote.getDriverId());
        if (null == vehicle || null == driver) {
            log.error("上报部平台，司机或车辆信息不存在");
            return;
        }

        String randomPublicKey = String.valueOf(this.redisUtil.get(appInfoDTO.getAppId() + "_randomPublicKey"));
        String randomPrivateKey = String.valueOf(this.redisUtil.get(appInfoDTO.getAppId() + "_randomPrivateKey"));
        String urlPublicKey = String.valueOf(this.redisUtil.get(appInfoDTO.getAppId() + "_publicKey"));
        String url = String.valueOf(this.redisUtil.get(appInfoDTO.getAppId() + "_url"));

        List<ShippingNoteInfo> list = new ArrayList<>();
        ShippingNoteInfo shippingNoteinfo = new ShippingNoteInfo();
        shippingNoteinfo.setAlreadySendCount(0);
        shippingNoteinfo.setSendCount(1);
        shippingNoteinfo.setSerialNumber("0000");
        shippingNoteinfo.setShippingNoteNumber(hyShippingNote.getShippingNoteNumber());
        shippingNoteinfo.setStartCountrySubdivisionCode(hyShippingNote.getSendAreaCode());
        shippingNoteinfo.setEndCountrySubdivisionCode(hyShippingNote.getReceiveAreaCode());
        shippingNoteinfo.setStartLocationText(hyShippingNote.getSendAddressName());
        shippingNoteinfo.setStartLongitude(Double.parseDouble(hyShippingNote.getSendLongitude().toString()));
        shippingNoteinfo.setStartLatitude(Double.parseDouble(hyShippingNote.getSendLatitude().toString()));
        shippingNoteinfo.setEndLocationText(hyShippingNote.getReceiveAddressName());
        shippingNoteinfo.setEndLongitude(Double.parseDouble(hyShippingNote.getReceiveLongitude().toString()));
        shippingNoteinfo.setEndLatitude(Double.parseDouble(hyShippingNote.getReceiveLatitude().toString()));
        list.add(shippingNoteinfo);

        LocationInfo locationInfo = new LocationInfo();
        locationInfo.setLongitude(Double.parseDouble(appLocationEvent.getLongitude()));
        locationInfo.setLatitude(Double.parseDouble(appLocationEvent.getLatitude()));
        locationInfo.setLocationText(appLocationEvent.getAddress());
        locationInfo.setLocationTime(System.currentTimeMillis());
        locationInfo.setLocationType(Integer.valueOf(appLocationEvent.getType()));
        locationInfo.setRemark(appLocationEvent.getRemark());
        locationInfo.setCountrySubdivisionCode(appLocationEvent.getAreaCode());
        locationInfo.setDeviceId(hyShippingNote.getDriverId());
        locationInfo.setDriverName(driver.getDriverName());
        locationInfo.setVehicleNumber(vehicle.getVehicleNumber());
        locationInfo.setRandomPublicKey(randomPublicKey);

        locationInfo.setShippingNoteInfos(list);
        List<LocationInfo> locationInfos = new ArrayList<>();
        locationInfos.add(locationInfo);


        String tkey = UUID.randomUUID().toString().replace("-", "").substring(0, 32);
        String encryptedCode = SM2Util.encrypt132(Util.hexToByte(tkey), Util.hexToByte(urlPublicKey));
        String data = JSONUtil.toJsonStr(locationInfos);
        String encryptedContent = LocationSM4Util.encryptEcb(tkey, data);

        EncryptionResponse response = new EncryptionResponse();
        response.setEncryptedCode(encryptedCode);
        response.setEncryptedContent(encryptedContent);

        LoctationInfoRequest request = new LoctationInfoRequest();
        request.setData(response);
        request.setRandomPublicKey(randomPublicKey);
        request.setAppId(appInfoDTO.getAppId());
        request.setAppSecurity(appInfoDTO.getAppSecurity());
        request.setEnterpriseSenderCode(appInfoDTO.getEnterpriseSenderCode());

        String result = HttpUtil.post(url, JSONUtil.toJsonStr(request));
        ResBaseBean resBaseBean = JSONUtil.toBean(result, ResBaseBean.class);
        log.info("上报部平台回执 <- {} ,{}", url, JSONUtil.toJsonStr(resBaseBean));
        if (resBaseBean.getSuccess()) {
            EncryptionResponse encryptionResponse = JSONUtil.toBean(resBaseBean.getData().toString(), EncryptionResponse.class);
//            String info1 = Decrypt.SM2Decrypt(randomPrivateKey, encryptionResponse.getEncryptedCode());
//            String plainText = Decrypt.SM4DecryptEcb(info1, encryptionResponse.getEncryptedContent());
            String info = Util.byteToHex(SM2Util.decrypt132(Util.hexToByte(randomPrivateKey), Util.hexToByte(encryptionResponse.getEncryptedCode())));
            String plainText = LocationSM4Util.decryptEcb(info, encryptionResponse.getEncryptedContent());

            log.info("上报部平台回执详细信息 <- {}", plainText);
        }

        //0成功 1失败
        HyShippingnoteLnglat shippingnoteLnglat =new HyShippingnoteLnglat();
        if (resBaseBean.getSuccess()) {
            shippingnoteLnglat.setState("0");
        } else {
            shippingnoteLnglat.setState("1");
        }
        shippingnoteLnglat.setType(appLocationEvent.getType());
        shippingnoteLnglat.setShippingNum(shippingNoteNumber);
        shippingnoteLnglat.setLongitude(Double.parseDouble(appLocationEvent.getLongitude()));
        shippingnoteLnglat.setLatitude(Double.parseDouble(appLocationEvent.getLatitude()));
        shippingnoteLnglat.setCreateTime(new Date());
        hyShippingnoteLnglatService.save(shippingnoteLnglat);
//      ResBaseBean resBaseBean = AppUtils.initUploadLocation(locationInfos, init);
        //结束位置上传

        //记录司机违规行为
        this.saveShippingNoteViolation(hyShippingNote, appLocationEvent.getType(),
                Double.parseDouble(appLocationEvent.getLongitude()), Double.parseDouble(appLocationEvent.getLatitude()));
        return;
    }


    /**
     * 记录司机违规行为
     * @param shippingNote
     * @param type
     * @param jingdu
     * @param weidu
     */
    private void saveShippingNoteViolation(HyShippingNote shippingNote ,String type ,double jingdu ,double weidu){
        HyShippingNoteViolation shippingNoteViolation = new HyShippingNoteViolation();
        shippingNoteViolation.setShippingNoteId(shippingNote.getId());
        shippingNoteViolation.setShippingNoteNumber(shippingNote.getShippingNoteNumber());
        shippingNoteViolation.setDriverId(shippingNote.getDriverId());
        shippingNoteViolation.setVehicleId(shippingNote.getVehicleId());
        shippingNoteViolation.setViolationTime(new Date());
        ScopeRequest scopeRequest = new ScopeRequest(jingdu, weidu);
        if("0".equals(type)) {
            String sendLongitude = shippingNote.getSendLongitude().toString();
            String sendLatitude = shippingNote.getSendLatitude().toString();
            boolean inPolygon = getPunch(scopeRequest, sendLongitude, sendLatitude);
            if (!inPolygon) {
                shippingNoteViolation.setViolationType("2");
                shippingNoteViolationService.save(shippingNoteViolation);
            }
        }
        if("1".equals(type)){
            String receiveLongitude =   shippingNote.getReceiveLongitude().toString() ;
            String receiveLatitude =    shippingNote.getReceiveLatitude().toString() ;
            boolean inPolygon = getPunch( scopeRequest , receiveLongitude , receiveLatitude);
            if(!inPolygon ){
                shippingNoteViolation.setViolationType("3");
                shippingNoteViolationService.save(shippingNoteViolation)  ;
            }
        }

        return;
    }

    /**
     *
     * @param platPunch 用户所在位置
     * @param longitudeS 设置目的地经度
     * @param latitudeS  设置目的纬度
     * @return
     */
    private static  boolean getPunch(ScopeRequest platPunch ,String longitudeS ,String latitudeS){
//        //1、设置目的地经度
//        String longitudeS = “106.235321”
//        //设置目的纬度
//        String latitudeS = “37.432579”;
        // 2、由前端传过来的用户所在位置 经纬度
        String longitudeT = platPunch.getLongitude().toString();
        String latitudeT = platPunch.getLatitude().toString();
        // 3、对比
        GlobalCoordinates source = new GlobalCoordinates(Double.parseDouble(latitudeS), Double.parseDouble(longitudeS));
        GlobalCoordinates target = new GlobalCoordinates(Double.parseDouble(latitudeT), Double.parseDouble(longitudeT));
        //这是两种坐标系计算方法，这是第一种坐标系计算方法（我们用的这种）
        double geoCurve = getDistanceMeter(source, target, Ellipsoid.Sphere);
        //这是两种坐标系计算方法，这是第二种坐标系计算方法
        double geoCurve2 = getDistanceMeter(source, target, Ellipsoid.WGS84);
        log.info(geoCurve + "----------" + geoCurve2);
        //如果用户和目的地位置相距10公里，在不能打卡；
        if (geoCurve > 10000){
            return false;
        } else{
            return true;
        }

    }

    /* 经纬度计算工具类*/
    public static double getDistanceMeter(GlobalCoordinates gpsFrom, GlobalCoordinates gpsTo, Ellipsoid ellipsoid)
    {
        //创建GeodeticCalculator，调用计算方法，传入坐标系、经纬度用于计算距离
        GeodeticCurve geoCurve = new GeodeticCalculator()
                .calculateGeodeticCurve(ellipsoid, gpsFrom, gpsTo);
        return geoCurve.getEllipsoidalDistance();
    }


}
